#!/usr/bin/env node

const fs = require('fs-extra');
const iconv = require('iconv-lite');
const path = require('path');
const moment = require('moment');
const command = require('./js/command');
const express = require('express');
const bodyParser = require('body-parser');
const utils = require('./js/utils');
const superagent = require('superagent');
const _ = require('lodash');
const program = require('commander');
const archiver = require("archiver");
const FormData = require("form-data");
const unzip = require("unzip-stream");
const streamRequest = require("stream-request");
const http = require('http');
const https = require('https');
const os = require('os');
os.tmpDir = os.tmpdir;
const configFile = path.join(os.homedir(), '.remote_config');
const CACHE = {};
let _url = 'http://localhost:4000';
let _user = 'fang';
let _pwd = '~';
let debug = false;

function joinURL(url, api) {
    return _.last(url) === '/' ?  `${url}${api}` : `${url}/${api}`;
}
function httpPost(api, data) {
    const url =  joinURL(_url, api);
    debug && console.log('[httpPost] send:', url, data);
    return new Promise(resolve => {
        superagent.post(url)
            .set({ 'Content-Type': `application/json;charset=UTF-8` })
            .send(data)
            .end((error, res) => {
                if (!error && res.statusCode === 200) {
                    const obj = utils.parseJSON(res.text);
                    debug && console.log('[httpPost] receive:', url, obj);
                    resolve(obj);
                } else {
                    debug && console.log('[httpPost] error:', url, error);
                    resolve({ message: error });
                }
            });
    });
}
function getFormatTime(time) {
    return time ? moment(time).format('YYYY-MM-DD HH:mm:ss') : undefined;
}
function startServer(url, root) {
    const formatPath = (path) => {
        path = path.replace(/^~/, root).replace(/\\/g, '/');
        if (!_.startsWith(path, root)) {
            console.log(`${path}是无效路径`);
            return { error: true };
        }
        return path;
    };
    const server = express();
    const app = express.Router();
    server.use(bodyParser.json());
    server.use((req, res, next) => {
        console.log(`[${req.url}]recv:`, req.query, req.body);
        next();
    });

    app.post("/getStatus", async (req, res) => {
        const file = formatPath(req.body.file);
        if (file.error) {
            return res.send({ message: '无效路径' });
        }
        if (!fs.existsSync(file)) {
            return res.send({ message: '该文件不存在' });
        }
        res.send({ success: true, context: { isFolder: fs.statSync(file).isDirectory() } });
    });
    app.post("/createFiles", async (req, res) => {
        const { list } = req.body;
        const errors = [];
        for (const item of list) {
            const file = formatPath(item);
            if (file.error) {
                errors.push(`${item}是无效路径`);
                continue;
            }
            if (fs.existsSync(file)) {
                errors.push(`${item}已经存在`);
                continue;
            }
            fs.ensureFileSync(file);
        }
        if (errors.length) {
            return res.send({ message: errors.join('，') });
        }
        res.send({ success: true });
    });
    app.post("/removeFiles", async (req, res) => {
        const { list } = req.body;
        const errors = [];
        for (const item of list) {
            const file = formatPath(item);
            if (file.error) {
                errors.push(`${item}是无效路径`);
                continue;
            }
            if (!fs.existsSync(file)) {
                errors.push(`${item}不存在`);
                continue;
            }
            fs.removeSync(file);
        }
        if (errors.length) {
            return res.send({ message: errors.join('，') });
        }
        res.send({ success: true });
    });
    app.post("/readFile", async (req, res) => {
        const file = formatPath(req.body.file);
        if (file.error) {
            return res.send({ message: '无效路径' });
        }
        if (!fs.existsSync(file)) {
            return res.send({ message: '文件不存在' });
        }
        const context = fs.readFileSync(file, 'utf-8');
        res.send({ success: true, context });
    });
    app.post("/writeFile", async (req, res) => {
        let { file, text, force } = req.body;
        file = formatPath(file);
        if (file.error) {
            return res.send({ message: '无效路径' });
        }
        if (!force && fs.existsSync(file)) {
            return res.send({ message: '文件已经存在' });
        }
        fs.writeFileSync(file, text, 'utf-8');
        res.send({ success: true });
    });
    app.post("/appendFile", async (req, res) => {
        let { file, text, force } = req.body;
        file = formatPath(file);
        if (file.error) {
            return res.send({ message: '无效路径' });
        }
        if (!force && fs.existsSync(file)) {
            return res.send({ message: '文件已经存在' });
        }
        fs.appendFileSync(file, text, 'utf-8');
        res.send({ success: true });
    });
    app.post("/rename", async (req, res) => {
        const originName = formatPath(req.body.originName);
        const targetName = formatPath(req.body.targetName);
        if (originName.error) {
            return res.send({ message: `${req.body.originName}是无效路径` });
        }
        if (targetName.error) {
            return res.send({ message: `${req.body.targetName}是无效路径` });
        }
        if (!fs.existsSync(originName)) {
            return res.send({ message: '文件不存在' });
        }
        if (fs.existsSync(targetName)) {
            if (!fs.statSync(targetName).isDirectory()) {
                return res.send({ message: '目标文件存在' });
            } else {
                if (fs.existsSync(path.join(targetName, path.basename(originName)))) {
                    return res.send({ message: '目标文件存在' });
                }
            }
        }
        fs.moveSync(originName, targetName);
        res.send({ success: true });
    });
    app.post("/copy", async (req, res) => {
        const originName = formatPath(req.body.originName);
        let targetName = formatPath(req.body.targetName);
        if (originName.error) {
            return res.send({ message: `${req.body.originName}是无效路径` });
        }
        if (targetName.error) {
            return res.send({ message: `${req.body.targetName}是无效路径` });
        }
        if (!fs.existsSync(originName)) {
            return res.send({ message: '文件不存在' });
        }
        if (fs.existsSync(targetName)) {
            if (!fs.statSync(targetName).isDirectory()) {
                return res.send({ message: `${req.body.targetName}已存在` });
            } else {
                targetName = path.join(targetName, path.basename(originName));
                if (fs.existsSync(targetName)) {
                    return res.send({ message: `${path.join(req.body.targetName, path.basename(originName))}已存在` });
                }
            }
        }
        fs.copySync(originName, targetName);
        res.send({ success: true });
    });
    app.post('/mkdirs', (req, res) => {
        const { list } = req.body;
        const errors = [];
        for (const item of list) {
            const file = formatPath(item);
            if (file.error) {
                errors.push(`${item}是无效路径`);
                continue;
            }
            fs.ensureDirSync(file);
        }
        if (errors.length) {
            return res.send({ message: errors.join('，') });
        }
        res.send({ success: true });
    });
    app.post("/getFileList", async (req, res) => {
        const folder = formatPath(req.body.folder);
        if (folder.error || !fs.existsSync(folder)) {
            return res.send([]);
        }
        const fileList = [];
        const children = [];
        const files = fs.readdirSync(folder);
        for (const file of files) {
            try {
                const info = fs.statSync(path.join(folder, file));
                if (info.isDirectory()) {
                    children.push({
                        isFolder: true,
                        fullname: path.join(req.body.folder, file).replace(/\\/g, '/'),
                        name: file,
                        uploadTime: getFormatTime(info.ctime),
                    });
                } else {
                    fileList.push({
                        name: file,
                        fullname: path.join(req.body.folder, file).replace(/\\/g, '/'),
                        size: info.size,
                        uploadTime: getFormatTime(info.ctime),
                    });
                }
            } catch (e) {
                console.log('error:', e);
            }
        }
        res.send([...children, ...fileList]);
    });
    app.post("/upload", streamRequest({ stream: "zipFile" }), async (req, res) => {
        const params = JSON.parse(req.body.params || "{}");
        console.log("upload recv:", params);
        const zip = req.body.zipFile;
        const out = unzip.Extract({ path: '.__tmp' });
        zip.pipe(out).on('close', async () => {
            let remote = formatPath(params.remote);
            if (remote.error) {
                fs.removeSync('.__tmp');
                return res.end(`${params.remote}是无效路径`);
            }
            let remotePath = path.dirname(remote);
            if (fs.existsSync(remote) && !fs.statSync(remote).isDirectory()) {
                fs.removeSync('.__tmp');
                return res.end('目标文件存在');
            }
            fs.ensureDirSync(remotePath);
            const files = fs.readdirSync('.__tmp');
            const list = [];
            files.forEach(function (file) {
                const filepath = '.__tmp/' + file;
                if (fs.existsSync(remote)) {
                    if (fs.existsSync(path.join(remote, file))) {
                        return list.push(`${path.join(params.remote, file)}已经存在`);
                    }
                    remote = path.join(remote, file);
                }
                fs.moveSync(filepath, remote);
            });
            fs.removeSync('.__tmp');
            res.end(list.length ? list.join(',') : '');
        });
    });
    app.get('/download', async (req, res) => {
        const file = formatPath(req.query.file);
        if (file.error) {
            return res.send({ message: '无效路径' });
        }
        if (!fs.existsSync(file)) {
            return res.send({ message: '无效路径' });
        }
        const zip = archiver("zip", { zlib: { level: 9 } });
        if (fs.statSync(file).isDirectory()) {
            zip.directory(file, path.basename(file));
        } else {
            zip.file(file, { name: path.basename(file) });
        }
        zip.pipe(res);
        zip.finalize();
    });
    app.post('/pasteToRemote', (req, res) => {
        const { text } = req.body;
        utils.copy(text);
        res.send({ success: true });
    });
    app.post('/copyFromRemote', (req, res) => {
        utils.paste(function (text) {
            res.send({ success: true, context: { text } });
        });
    });
    app.post('/disconnect', (req, res) => {
        res.send("success");
        process.nextTick(() => { process.exit() });
    });
    url = new URL(url);
    server.use(url.pathname, app);
    server.listen(url.port).on('listening', () => {
        console.log(`http://${utils.getIPAdress()}:${url.port}${url.pathname} with path: ${root}`);
    });
}
async function createRemote(commands, options) {
    // 自动完成
    async function completer(line, callback) {
        const words = line.trim().split(/\s/);
        let list = [];
        if (words.length <= 1) {
            const cmds = ['help', 'cls', 'pwd', 'whoami', 'ls', 'll', 'mkdir', 'cd', 'cp', 'mv', 'rm', 'touch', 'echo', 'cat', 'pull', 'push', 'xcat', 'lg', 'copyFromRemote', 'pasteToRemote'];
            const _linePartial = line.trim();
            list = cmds.filter((o) => o.startsWith(_linePartial));
            return callback(null, [list, _linePartial]);
        }
        const lastWord = _.last(words);
        const items = lastWord.split('/');
        const linePartial = _.last(items);
        const prepath = items.slice(0, -1).join('/');
        const firstWord = words[0];
        if (_.includes(['mv', 'rm', 'cat', 'pull', 'push', 'xcat', 'lg'], firstWord)) {
            list = await getAllFiles(fs, prepath);
        } else if (_.includes(['ls', 'll', 'mkdir', 'cd', 'touch', 'echo'], firstWord)) {
            list = await getDirs(fs, prepath);
        }
        const found = list.filter((o) => o.startsWith(linePartial));
        return callback(null, [found.length ? found : list, linePartial]);
    }
    CACHE._options = { ...options, fs, completer };
    command(commands, CACHE._options);
}
function getSize(number) {
    if (!number) {
        return number === undefined ? '' : '0B';
    }
    const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    const index = Math.floor(Math.log(number) / Math.log(1024));
    if (!index) {
        return number + 'B';
    }
    return (number / Math.pow(1024, index)).toFixed(2) + units[index];
}
async function getAllFiles(fs, path) {
    if (CACHE.lastAllPath && CACHE.lastAllPath === path) {
        return CACHE.allFiles;
    }
    let list = await httpPost('getFileList', { folder: getPath(path) });
    list = list.map(o => o.name);
    CACHE.lastAllPath = path;
    CACHE.allFiles = list;
    return list;
}
async function getDirs(fs, path) {
    if (CACHE.lastDirPath && CACHE.lastDirPath === path) {
        return CACHE.dirs;
    }
    let list = await httpPost('getFileList', { folder: getPath(path) });
    list = _.filter(list, o => o.isFolder).map(o => o.name);
    CACHE.lastDirPath = path;
    CACHE.dirs = list;
    return list;
}
async function getFiles(fs, path) {
    if (CACHE.lastFilePath && CACHE.lastFilePath === path) {
        return CACHE.files;
    }
    let list = await httpPost('getFileList', { folder: getPath(path) });
    list = _.filter(list, o => !o.isFolder).map(o => o.name);
    CACHE.lastFilePath = path;
    CACHE.files = list;
    return list;
}
function getPath(line) {
    return /^~/.test(line) ? line : path.join(_pwd, line);
}
function pwd(line = '', { fs }) {
    _pwd = path.join(_pwd, line);
    this.print(`${_pwd}`, 'gray');
    this.prompt();
}
function whoami(line, { fs }) {
    this.print(`${_user}`, 'gray');
    this.prompt();
}
function cd(line = '', { fs }) {
    if (!line) {
        _pwd = '/';
    } else {
        _pwd = path.join(_pwd, line);
    }
    this.setPrompt(`remote@${_user}:${_pwd} $\n`);
    this.prompt();
}
async function xls(line = '.', { fs }) {
    const fileList = [];
    const children = [];
    const folder = line;
    if (!fs.existsSync(folder)) {
        return this.error('无效路径');
    }
    if (!fs.statSync(folder).isDirectory()) {
        return this.printArray([{ msg: folder, color: 'green' }]);
    }
    const files = fs.readdirSync(line);
    for (const file of files) {
        const fullname = path.join(folder, file);
        try {
            const info = fs.statSync(fullname);
            if (info.isDirectory()) {
                children.push({ isFolder: true, name: file });
            } else {
                fileList.push({ name: file });
            }
        } catch (e) {
            console.log('error:', e);
        }
    }
    this.printArray([...children, ...fileList].map(o => ({ msg: o.name + '  ', color: o.isFolder ? 'blue' : 'green' })));
    this.prompt();
}
async function ls(line = '', { fs }) {
    const list = await httpPost('getFileList', { folder: getPath(line) });
    this.printArray(list.map(o => ({ msg: o.name + '  ', color: o.isFolder ? 'blue' : 'green' })));
    this.prompt();
}
async function ll(line = '', { fs }) {
    const list = await httpPost('getFileList', { folder: getPath(line) });
    let texts = [];
    texts[0] = { msg: _.padEnd('name', 30), color: 'black' };
    texts[1] = { msg: _.padEnd('size', 10), color: 'black' };
    texts[2] = { msg: _.padEnd('accessTime', 25), color: 'black' };
    texts[3] = { msg: 'fullname', color: 'black' };
    this.printArray(texts);
    for (const file of list) {
        texts = [];
        texts[0] = { msg: _.padEnd(file.name, 30), color: file.isFolder ? 'blue' : 'green' };
        texts[1] = { msg: _.padEnd(getSize(file.size), 10), color: 'gray' };
        texts[2] = { msg: _.padEnd(moment(file.uploadTime).format('YYYY-MM-DD HH:mm:ss'), 25), color: 'gray' };
        texts[3] = { msg: file.fullname, color: 'gray' };
        this.printArray(texts);
    }
    this.prompt();
}
async function mkdir(line = '', { fs }) {
    if (!line) {
        return this.error('use mkdir xx[ yy]');
    }
    const list = line.split(/\s+/).map(o => getPath(o));
    const result = await httpPost('mkdirs', { list });
    if (!result.success) {
        this.error(result.message);
    } else {
        this.prompt();
    }
}
async function mv(line = '', { fs }) {
    if (!line) {
        return this.error('no file');
    }
    const list = line.split(/\s+/);
    const result = await httpPost('rename', { originName: getPath(list[0]), targetName: getPath(list[1]) });
    if (!result.success) {
        this.error(result.message);
    } else {
        this.prompt();
    }
}
async function cp(line = '', { fs }) {
    if (!line) {
        return this.error('no file');
    }
    const list = line.split(/\s+/);
    const result = await httpPost('copy', { originName: getPath(list[0]), targetName: getPath(list[1]) });
    if (!result.success) {
        this.error(result.message);
    } else {
        this.prompt();
    }
}
async function rm(line = '', { fs }) {
    if (!line) {
        return this.error('no file');
    }
    const list = line.split(/\s+/).map(o => getPath(o));
    const result = await httpPost('removeFiles', { list });
    if (!result.success) {
        this.error(result.message);
    } else {
        this.prompt();
    }
}
async function touch(line = '', { fs }) {
    if (!line) {
        return this.error('no file');
    }
    const list = line.split(/\s+/).map(o => getPath(o));
    const result = await httpPost('createFiles', { list });
    if (!result.success) {
        this.error(result.message);
    } else {
        this.prompt();
    }
}
async function echo(line = '', { fs }) {
    const matches = line.match(/(.*[^>])(>{1,2})\s*([.\w]+)$/);
    if (matches) {
        const text = matches[1];
        const op = matches[2];
        const file = matches[3];
        if (op === '>') {
            const result = await httpPost('writeFile', { file: getPath(file), text });
            if (!result.success) {
                this.error(result.message);
            } else {
                this.prompt();
            }
        } else {
            const result = await httpPost('appendFile', { file: getPath(file), text });
            if (!result.success) {
                this.error(result.message);
            } else {
                this.prompt();
            }
        }
    } else {
        this.print(`${line}`, 'gray');
        this.prompt();
    }
}
function lg(line = '', { fs }) {
    if (!line) {
        return this.error('no file');
    }
    utils.paste(async text => {
        const result = await httpPost('writeFile', { file: getPath(line), text });
        if (!result.success) {
            this.error(result.message);
        } else {
            this.prompt();
        }
    });
}
async function copyFromRemote(line, { fs }) {
    const result = await httpPost('copyFromRemote');
    if (!result.success) {
        this.error(result.message);
    } else {
        utils.copy(result.context.text);
        this.prompt();
    }
}
async function pasteToRemote(line, { fs }) {
    utils.paste(async text => {
        const result = await httpPost('pasteToRemote', { text });
        if (!result.success) {
            this.error(result.message);
        } else {
            this.prompt();
        }
    });
}
async function cat(line = '', { fs }) {
    if (!line) {
        return this.error('no file');
    }
    const result = await httpPost('readFile', { file: getPath(line) });
    if (!result.success) {
        this.error(result.message);
    } else {
        this.print(result.context, 'gray');
        this.prompt();
    }
}
async function xcat(line = '', { fs }) {
    if (!line) {
        return this.error('no file');
    }
    const result = await httpPost('readFile', { file: getPath(line) });
    if (!result.success) {
        this.error(result.message);
    } else {
        utils.copy(result.context);
        this.prompt();
    }
}
async function disconnect(line, { fs }) {
    const result = await httpPost('disconnect');
    if (!result.success) {
        this.error(result.message);
    } else {
        utils.copy(result.context);
        this.prompt();
    }
}
async function pull(line = '', { fs }) {
    if (!line) {
        return this.error('no file');
    }
    let remote, localPath, localName;
    if (/.*\s+.*/.test(line)) {
        const list = line.split(/\s+/);
        remote = list[0];
        remoteName = path.basename(remote);
        localPath = path.dirname(list[1]);
        localName = path.basename(list[1]);
    } else {
        remote = line;
        remoteName = path.basename(remote);
        localPath = '.';
        localName = path.basename(remote);
    }
    let local = path.join(localPath, localName);
    local = local.replace(/^~/, os.homedir());
    if (fs.existsSync(local) && !fs.statSync(local).isDirectory()) {
        return this.error('本地文件已经存在');
    }
    const result = await httpPost('getStatus', { file: getPath(remote) });
    if (!result.success) {
        return this.error(result.message);
    }
    const url = new URL(_url);
    const options = { hostname: url.hostname, port: url.port, path: encodeURI(`${joinURL(url.pathname, 'download')}?file=${getPath(remote)}`), agent: false };
    (url.protocol === 'http:' ? http : https).get(options, res => {
        const out = unzip.Extract({ path: '.__tmp' });
        res.pipe(out);
        out.on('close', async () => {
            fs.ensureDirSync(localPath);
            const files = fs.readdirSync('.__tmp');
            const list = [];
            files.forEach(function (file) {
                const filepath = '.__tmp/' + file;
                if (fs.existsSync(local)) {
                    if (fs.existsSync(path.join(local, file))) {
                        return list.push(`${path.join(local, file)}已经存在`);
                    }
                    local = path.join(local, file);
                }
                fs.moveSync(filepath, local);
            });
            fs.removeSync('.__tmp');
            if (list.length) {
                return this.error(list.join(','));
            }
            this.prompt();
        });
    }).on('error', e => {
        this.error(e.message);
    });
}
async function push(line = '', { fs }) {
    if (!line) {
        return this.error('no file');
    }
    let local, remote;
    if (/.*\s+.*/.test(line)) {
        const list = line.split(/\s+/);
        local = list[0];
        remote = getPath(list[1]); // 对应 push y ~/x/z -> ~/x/z, push y, x/z -> ${cwd}/x/z 的情况
    } else {
        local = line;
        remote = getPath(path.basename(local)); // 当前目录，对应 push x/y -> ${cwd}/y 的情况
    }
    local = local.replace(/^~/, os.homedir());
    if (!fs.existsSync(local)) {
        return this.error('本地文件不存在');
    }
    // push x/y -> ${cwd}/y, push y ~/x/z -> ~/x/z, push y, x/z -> ${cwd}/x/z
    const zip = archiver("zip", { zlib: { level: 9 } });
    const stat = fs.statSync(local);
    if (stat.isDirectory()) {
        zip.directory(local, path.basename(local));
    } else {
        zip.file(local, { name: path.basename(local) });
    }

    const formData = new FormData();
    formData.append("params", JSON.stringify({ remote }));
    formData.append("zipFile", zip, {
        filename: "temp.zip",
        filepath: "temp.zip",
        contentType: "application/x-zip-compressed",
    });
    const formHeaders = formData.getHeaders();
    const url = new URL(_url);
    const options = { hostname: url.hostname, port: url.port, path: joinURL(url.pathname, 'upload'), method: 'POST', headers: formHeaders };
    let buffer = [];
    const request = (url.protocol === 'http:' ? http : https).request(options, res => {
        res.on('end', () => {
            const error = Buffer.concat(buffer).toString('utf-8');
            if (error) {
                this.error(error);
            }
            this.prompt();
        }).on('data', (buf) => {
            buffer.push(buf);
        }).on('error', (e) => {
            this.error(e.message);
        });
    });
    formData.pipe(request);
    zip.finalize();
}
function changeDebug() {
    debug = !debug;
    this.success(`debug set to ${debug}`);
}
function showHelp() {
    this.print('commands:', 'blue');
    this.print('    h: show help');
    this.print('    help: show full help');
    this.print('    <q|exit>: exit');
    this.print('    whoami pwd cd ls ll xls mkdir mv rm touch echo cat pull push xcat lg copyFromRemote pasteToRemote debug');
    this.print('    pull remote [local] (pull x/y->./yy  pull x/y ~/x/z -> ~/x/z[/y])');
    this.print('    push local [remote] (push x/y -> ${cwd}/y, push y ~/x/z -> ~/x/z, push y, x/z -> ${cwd}/x/z)');
    this.print('');
    this.prompt();
}
const COMMANDS = {
    'h': showHelp,
    'help': showHelp,
    'q|exit': function () { this.close() },
    'cls': function () { this.clear() },
    'pwd': pwd,
    'whoami': whoami,
    'xls': xls,
    'ls': ls,
    'll': ll,
    'mkdir': mkdir,
    'cd': cd,
    'mv': mv,
    'cp': cp,
    'rm': rm,
    'touch': touch,
    'echo': echo,
    'cat': cat,
    'pull': pull,
    'push': push,
    'xcat': xcat,
    'lg': lg,
    'copyFromRemote': copyFromRemote,
    'pasteToRemote': pasteToRemote,
    'disconnect': disconnect,
    'debug': changeDebug,
};

const serverList = [
    { url: "https://ifufan.com", label: "212外网服务器" },
    { url: "http://api.ifufan.com:1192", label: "168前置机" },
    { url: "http://128.0.250.152:1112", label: "华山128" },
    { url: "http://128.0.250.156:1112", label: "华山156" },
    { url: "http://api.ifufan.com:12168", label: "华山168" },
    { url: "http://api.ifufan.com:12201", label: "华山201" },
    { url: "http://api.ifufan.com:12001", label: "FF" },
    { url: "http://124.223.42.41:1112", label: "虚拟HIS服务器" },
    { url: "http://124.223.49.85:1112", label: "虚拟业务服务器" },
    { url: "http://124.222.144.66:1112", label: "虚拟数仓服务器" },
    { url: "http://112.65.72.196:1112", label: "方舱医院前置机" },
];

program
    .version('1.0.0')
    .option('-u, --url [http://localhost:4000]', 'set url', '')
    .option('-r, --user [user]', 'set user', '')
    .option('-s, --server [root]', 'start server with root path', '')
    .option('-c, --config', 'show config', '')
    .option('-l, --list', 'set server [index]', '')
    .parse(process.argv);

let options = {};
try {
    options = fs.readJSONSync(configFile);
} catch (e) { }
_url = program.url || options.url || _url;
if (program.list === true) {
    if (!program.args.length) {
        _.forEach(serverList, (o, k) => console.log(`${k}: ${o.label}(${o.url})`));
        process.exit(0);
    } else {
        const item = serverList[program.args[0]];
        if (!item) {
            console.log('请选择正确的服务器');
            process.exit(0);
        }
        _url = `${item.url}/xsfs`;
    }
}
_user = program.user || options.user || _user;
if (program.config) {
    console.log({ url: _url, user: _user });
    process.exit(0);
}
if (options.url !== _url || options.user !== _user) {
    fs.writeJSONSync(configFile, { url: _url, user: _user });
}
if (!program.server && _.includes(process.argv.slice(2), '-s')) {
    program.server = true;
}
if (program.server) {
    let root = process.cwd().replace(/\\/g, '/');
    if (program.server !== true) {
        root = program.server.replace(/^~/, os.homedir());
        if (!fs.existsSync(root)) {
            console.error(`${program.server} 不存在`);
            process.exit(-1);
        }
        root = path.resolve(root).replace(/\\/g, '/');
    }
    startServer(_url, root);
} else {
    createRemote(COMMANDS, { title: 'remote', prompt: `remote@${_user}:${_pwd} $\n`, history: ['h', 'help', 'cls', 'pwd', 'whoami', 'xls', 'ls', 'll', 'mkdir', 'cd', 'mv', 'rm', 'cp', 'touch', 'echo', 'cat', 'pull', 'push', 'xcat', 'lg', 'copyFromRemote', 'pasteToRemote'] });
}
